from sys import argv
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

class MyPyOpenGLTest:
    def __init__(self, width=640, height=480, title='多层次选择物体'):
        glutInit(len(argv), argv)
        glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
        glutInitWindowSize(width, height)
        glutInitWindowPosition(350, 200)
        self.window = glutCreateWindow(title.encode('gbk'))
        glutDisplayFunc(self.draw)
        glutMouseFunc(self.mouse_downup)

        self.InitGL(width, height)
        # 要绘制的三维物体
        self.objects = ('', '人物1', '人物2', '头部',
                        '左臂', '右臂', '左腿', '右腿',
                        '左脚', '右脚', '左手', '右手')
    # 根据特定的需要，进一步完成OpenGL的初始化
    def InitGL(self, width, height):
        # 初始化窗口背景为灰色
        glClearColor(0.6, 0.6, 0.6, 1)
        # 启用深度测试
        glEnable(GL_DEPTH_TEST)
        glClearDepth(5)
        glDepthFunc(GL_LESS)

        # 设置光滑渲染模式
        glShadeModel(GL_SMOOTH)
        glEnable(GL_POINT_SMOOTH)
        glEnable(GL_LINE_SMOOTH)
        glEnable(GL_POLYGON_SMOOTH)
        glHint(GL_POINT_SMOOTH_HINT, GL_NICEST)
        glHint(GL_LINE_SMOOTH_HINT, GL_NICEST)
        glHint(GL_POLYGON_SMOOTH_HINT, GL_FASTEST)

        # 设置光照效果
        glLightfv(GL_LIGHT0, GL_POSITION, (0, 3, 3, 0))
        glLightfv(GL_LIGHT0, GL_AMBIENT, (0.1, 0.1, 0.1, 1))
        glLightfv(GL_LIGHT0, GL_DIFFUSE, (0.6, 0.6, 0.6, 1))
        glEnable(GL_LIGHTING)
        glEnable(GL_LIGHT0)
        # 使得材质对当前光照进行跟踪
        glEnable(GL_COLOR_MATERIAL)
        glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE)

        glMatrixMode(GL_PROJECTION)
        gluPerspective(45.0, width / height, 1, 300)
        glMatrixMode(GL_MODELVIEW)

    def mouse_downup(self, button, state, x, y):
        if button == GLUT_LEFT_BUTTON and state == GLUT_UP:
            self.do_selection(x, y)

    def do_selection(self, x, y):
        #
        selection_buffer = glSelectBuffer(16)

        # 结果与glViewport（）函数的参数一致
        viewport = glGetIntegerv(GL_VIEWPORT)
        # 保存当前的投影矩阵
        glMatrixMode(GL_PROJECTION)
        glPushMatrix()
        # 切换为选择模式
        glRenderMode(GL_SELECT)
        glLoadIdentity()

        # 定义拾取区域，邻域越小识别越精确，邻域越大识别越粗糙
        # 参数分别为拾取位置的x坐标、y坐标、dx、dy，dx和dy表示邻域大小
        gluPickMatrix(x, viewport[3]-y, 2, 2, viewport)

        # 计算宽高比，设置景视体
        aspect = viewport[2] / viewport[3]

        gluPerspective(45, aspect, 1, 300)
        self.draw()

        hits = glRenderMode(GL_RENDER)
        if hits:
            for index in range(len(selection_buffer))[::-1]:
                if selection_buffer[index] >= len(self.objects):
                    break
            selected = filter(None, selection_buffer[index+1:])
            label = "-".join(map(self.objects.__getitem__, selected))
            glutSetWindowTitle(f'选择物体-选中了{label}'.encode('gbk'))
        else:
            glutSetWindowTitle("选择物体-没选择任何物体".encode('gbk'))
        # 恢复投影矩阵

        glMatrixMode(GL_PROJECTION)
        glPopMatrix()
        glMatrixMode(GL_MODELVIEW)






    def draw(self):
        '''自己的绘图方法，本程序的关键'''
        # 每次绘制图形之前清除颜色缓冲区和深度缓冲区中的信息
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        # 切换到模型观察矩阵，才能正确绘制图形
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity
        glInitNames()
        glPushName(0)

        #绘制第一个人物
        glColor3f(0, 0, 1)
        glTranslatef(-30, 0, -250)

        glLoadName(self.objects.index('人物1'))
        glutSolidSphere(20, 30, 30)
        glTranslatef(0, 25, 0)
        glPushName(self.objects.index('头部'))
        glutSolidSphere(8, 30, 30)
        glPopName()

        glLoadIdentity()
        glRotate(60, 0, 0, 1)
        glScale(1, 0.5, 0.5)
        glTranslatef(-22, 25, -250)
        glColor3f(1, 1, 0)
        glPushName(self.objects.index('左腿'))
        glutSolidSphere(5, 20, 20)
        glTranslatef(-5, 0, 0)
        glPushName(self.objects.index('左脚'))
        glutSolidSphere(2, 10, 10)
        glPopName()
        glPopName()

        glLoadIdentity()
        glRotate(-60, 0, 0, 1)
        glScale(1, 0.5, 0.5)
        glTranslatef(7, -25, -250)
        glPushName(self.objects.index('右腿'))
        glutSolidSphere(5, 20, 20)
        glPopName()

        glLoadIdentity()
        glRotate(-30, 0, 0, 1)
        glScale(1, 0.5, 0.5)
        glTranslatef(-28, -17, -250)
        glColor3f(1, 1, 0)
        glPushName(self.objects.index('左臂'))
        glutSolidSphere(5, 20, 20)
        glPopName()

        glLoadIdentity()
        glRotate(30, 0, 0, 1)
        glScale(1, 0.5, 0.5)
        glTranslatef(1, 15, -250)
        glPushName(self.objects.index('右臂'))
        glutSolidSphere(5, 20, 20)
        glPopName()


        # 绘制第二个人物
        glLoadIdentity()
        glColor3f(0, 0, 1)
        glTranslatef(50, 0, -250)

        #
        glLoadName(self.objects.index('人物2'))
        glutSolidSphere(20, 30, 30)
        glTranslatef(0, 25, 0)

        glPushName(self.objects.index('头部'))
        glutSolidSphere(8, 30, 30)
        glPopName()

        glLoadIdentity()
        glRotate(60, 0, 0, 1)
        glScale(1, 0.5, 0.5)
        glTranslatef(-2, -45, -250)
        glColor3f(1, 1, 0)
        glPushName(self.objects.index('左腿'))
        glutSolidSphere(5, 20, 20)
        glTranslatef(-5, 0, 0)
        glPushName(self.objects.index('左脚'))
        glutSolidSphere(2, 10, 10)
        glPopName()
        glPopName()
        glLoadIdentity()

        glRotate(-60, 0, 0, 1)
        glScale(1, 0.5, 0.5)
        glTranslatef(27, 46, -250)
        glPushName(self.objects.index('右腿'))
        glutSolidSphere(5, 20, 20)
        glPopName()
        glLoadIdentity()

        glRotate(-30, 0, 0, 1)
        glScale(1, 0.5, 0.5)
        glTranslatef(7, 22, -250)
        glColor3f(1, 1, 0)
        glPushName(self.objects.index('左臂'))
        glutSolidSphere(5, 20, 20)
        glPopName()

        glLoadIdentity()
        glRotate(30, 0, 0, 1)
        glScale(1, 0.5, 0.5)
        glTranslatef(36, -26, -250)
        glColor3f(1, 1, 0)
        glPushName(self.objects.index('右臂'))
        glutSolidSphere(5, 20, 20)
        glLoadIdentity()

        # 交换缓冲区，更新图形
        glutSwapBuffers()

    # 消息主循环
    def mainloop(self):
        glutMainLoop()

if __name__ == '__main__':
    # 创建窗口对象，启动消息主循环
    MyPyOpenGLTest().mainloop()
